﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using System.Data.Objects.DataClasses;
using System.Reflection;
using System.Diagnostics;

namespace EntityFrameworkContrib.PostSharp4EF
{
    /// <summary>
    /// intercepts calls to collection properties. 
    /// Will use the EDM relationship manager do it's work.
    /// However, during deserialization, normal field storage is used. That is
    /// necessary because we have to be able to initialize the manager ourselves. 
    /// We need both original values and current values at that point without
    /// interference from the manager
    /// </summary>
    [Serializable]
    class OnRelatedReferenceFieldAspect : OnFieldAccessAspect
    {
        string relationshipname, targetrolename;

        public OnRelatedReferenceFieldAspect(string relationshipname, string targetrolename, Type fieldType)
        {
            this.relationshipname = relationshipname;
            this.targetrolename = targetrolename;
        }

        public override void OnGetValue(FieldAccessEventArgs eventArgs)
        {
            if (!((ISerializationHelp)eventArgs.Instance).IsDeserializing())
            {

                RelationshipManager manager = ((IEntityWithRelationships)eventArgs.Instance).RelationshipManager;
                IRelatedEnd relatedEnd = manager.GetRelatedEnd(relationshipname, targetrolename);

                object entityReferenceValue = null;
                foreach (object o in relatedEnd)
                {
                    Debug.Assert(entityReferenceValue == null, "EntityReferences should only have one value");
                    entityReferenceValue = o;
                }
                eventArgs.ExposedFieldValue = entityReferenceValue;


                // original method
                //eventArgs.ExposedFieldValue = ((IEntityWithRelationships)eventArgs.Instance).RelationshipManager.
                //  GetRelatedReference<IEntityWithRelationships>(relationshipname, targetrolename).Value;
            }
            else
            {
                eventArgs.ExposedFieldValue = eventArgs.StoredFieldValue;
            }
        }

        public override void OnSetValue(FieldAccessEventArgs eventArgs)
        {
            if (!((ISerializationHelp)eventArgs.Instance).IsDeserializing())
            {
                RelationshipManager manager = ((IEntityWithRelationships)eventArgs.Instance).RelationshipManager;
                IRelatedEnd relatedEnd = manager.GetRelatedEnd(relationshipname, targetrolename);

                // first remove previous entityReference
                IEntityWithRelationships currentEntityReferenceValue = null;
                foreach (object o in relatedEnd)
                {
                    Debug.Assert(currentEntityReferenceValue == null, "EntityReferences should only have one value");
                    Debug.Assert(o is IEntityWithRelationships);
                    currentEntityReferenceValue = (IEntityWithRelationships)o;
                }
                if (currentEntityReferenceValue != null)
                {
                    relatedEnd.Remove(currentEntityReferenceValue);
                }

                if (eventArgs.ExposedFieldValue != null)
                {
                    relatedEnd.Add((IEntityWithRelationships)eventArgs.ExposedFieldValue);
                }
                // original method
                //manager.GetRelatedReference<IEntityWithRelationships>(relationshipname, targetrolename).Value = (IEntityWithRelationships)eventArgs.ExposedFieldValue;
            }
            else
            {
                eventArgs.StoredFieldValue = eventArgs.ExposedFieldValue;
            }
        }


        public override OnFieldAccessAspectOptions GetOptions()
        {
            return OnFieldAccessAspectOptions.None;
        }

    }
}
